package com.domdd.enums.upload.sos;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.domdd.util.ObjectFieldHandler;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public interface CommonUpload {

    Logger log = LoggerFactory.getLogger(CommonUpload.class);

    String getColumnName();

    void setColumnName(String columnName);

    Integer getIndex();

    void setIndex(Integer index);

    Class getValueType();

    void setValueType(Class valueType);

    Integer getMaxLength();

    void setMaxLength(Integer maxLength);

    String getPattern();

    void setPattern(String pattern);

    Boolean getIsNotNull();

    void setIsNotNull(Boolean isNotNull);

    String getPatternErrorMsg();

    void setPatternErrorMsg(String patternErrorMsg);

    ThreadPoolExecutor UPLOAD_THREAD_POOL = new ThreadPoolExecutor(200, 300,
            3 * 60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(500),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy());

    static void submitUploadTask(List<Runnable> runnableList, CountDownLatch cdl) {
        if (CollectionUtil.isEmpty(runnableList)) {
            return;
        }
//        ThreadPoolExecutor pool = new ThreadPoolExecutor(runnableList.size(), runnableList.size(),
//                0L, TimeUnit.MILLISECONDS,
//                new LinkedBlockingQueue<>(500),
//                Executors.defaultThreadFactory(),
//                new ThreadPoolExecutor.CallerRunsPolicy());
//        runnableList.forEach(pool::submit);
//        pool.shutdown();
        runnableList.forEach(UPLOAD_THREAD_POOL::submit);
        try {
            cdl.await();
        } catch (InterruptedException ignored) {
        }
    }

    @SuppressWarnings("all")
    static <E extends CommonUpload> Map<String, Collection<String>> getErrorMsgByRow(E[] values, Row r) {
        //empty
        List<E> notNullEnumList = ObjectFieldHandler.generateFindAllOptional(values, E::getIsNotNull);
        Collection<String> notNullCheck = ObjectFieldHandler.generateDistinctListByObj(ObjectFieldHandler.generateFindAllOptional(notNullEnumList,
                        notNull -> !ObjectFieldHandler.isNotBlank(getValueByRow(r, notNull))),
                E::getColumnName);

        //length
        List<E> maxLengthEnumList = ObjectFieldHandler.generateFindAllOptional(values, v -> ObjectUtil.isNotNull(v.getMaxLength()));
        Collection<String> lengthCheck = ObjectFieldHandler.generateDistinctListByObj(ObjectFieldHandler.generateFindAllOptional(maxLengthEnumList,
                        maxLength -> ObjectFieldHandler.isNotBlank(getValueByRow(r, maxLength)) && !notNullCheck.contains(getValueByRow(r, maxLength)) && getValueByRow(r, maxLength).toString().length() > maxLength.getMaxLength()),
                E::getColumnName);

        //parttern
        List<E> patternList = ObjectFieldHandler.generateFindAllOptional(values, v -> ObjectUtil.isNotNull(v.getPattern()));
        Collection<String> patternCheck = ObjectFieldHandler.generateDistinctListByObj(ObjectFieldHandler.generateFindAllOptional(patternList,
                        pattern -> ObjectFieldHandler.isNotBlank(getValueByRow(r, pattern)) && !notNullCheck.contains(getValueByRow(r, pattern)) && !maxLengthEnumList.contains(getValueByRow(r, pattern))
                                && !getValueByRow(r, pattern).toString().matches(pattern.getPattern())),
                E::getColumnName);
        return ImmutableMap.of("notNullCheck", notNullCheck, "lengthCheck", lengthCheck, "patternCheck", patternCheck);
    }

//    static void parseFormatErrorList(Map<String, Collection<String>> formatResult, List<ModelUploadVo.RowErrorMsg> errorMsgList, String name, Integer rowNum) {
//        parseFormatErrorList(formatResult, errorMsgList, name, null, rowNum);
//    }
//
//    static void parseFormatErrorList(Map<String, Collection<String>> formatResult, List<ModelUploadVo.RowErrorMsg> errorMsgList, String name, Integer sheetNum, Integer rowNum) {
//        if (CollectionUtil.isNotEmpty(formatResult.get("notNullCheck"))) {
//            errorMsgList.add(new ModelUploadVo.RowErrorMsg(name, sheetNum, rowNum,
//                    StrUtil.join(",", formatResult.get("notNullCheck")) + " 内容为必填项,请填写."));
//        }
//        if (CollectionUtil.isNotEmpty(formatResult.get("lengthCheck"))) {
//            errorMsgList.add(new ModelUploadVo.RowErrorMsg(name, sheetNum, rowNum,
//                    StrUtil.join(",", formatResult.get("lengthCheck")) + " 长度超出限制,请重新填写."));
//        }
//        if (CollectionUtil.isNotEmpty(formatResult.get("patternCheck"))) {
//            errorMsgList.add(new ModelUploadVo.RowErrorMsg(name, sheetNum, rowNum,
//                    "格式错误，请对照模板中格式要求调整：" + StrUtil.join(",", formatResult.get("patternCheck"))));
//        }
//    }

    static <E extends CommonUpload> Boolean checkRowOptional(E[] values, Row r) {
        return checkRowOptional(values, r, true);
    }

    static <E extends CommonUpload> Boolean checkRowOptional(E[] values, Row r, Boolean isCheckFormat) {
        return checkRowOptional(values, r, isCheckFormat, new HashMap<>());
    }

    static <E extends CommonUpload> Boolean checkRowOptional(E[] values, Row r, Boolean isCheckFormat, Map<String, Integer> headMap) {
        if (ArrayUtil.isEmpty(values)) {
            return checkRowOptional(r);
        }
        Boolean isNotEmpty = ObjectFieldHandler.generateFindAnyOptional(values, v -> ObjectFieldHandler.isNotBlank(getStrValByRow(r, v, headMap))) != null;
        if (BooleanUtil.isTrue(isNotEmpty) && BooleanUtil.isTrue(isCheckFormat)) {
            Arrays.stream(values).forEach(v -> {
                String value = getStrValByRow(r, v, headMap);
                if (BooleanUtil.isTrue(v.getIsNotNull()) && StrUtil.isBlank(value)) {
                    throw new RuntimeException("No." + (r.getRowNum() + 1) + " line, [" + v.getColumnName() + "] couldn't be empty");
                }
                if (ObjectUtil.isNotNull(value) && StrUtil.isNotBlank(v.getPattern())) {
                    if (!value.matches(v.getPattern())) {
                        throw new RuntimeException("No." + (r.getRowNum() + 1) + " line, [" + v.getColumnName() + "] format error," +
                                (StrUtil.isNotBlank(v.getPatternErrorMsg()) ? v.getPatternErrorMsg() + "," : "")
                                + " please check");
                    }
                }
            });
        }
        return isNotEmpty;
    }

    static Boolean checkRowOptional(Row r) {
        if (r == null) {
            return false;
        }
        return ObjectFieldHandler.generateFindAnyOptional(r.cellIterator(), cell -> {
            setCellStrType(cell);
            return ObjectFieldHandler.isNotBlank(cell.getStringCellValue());
        }) != null;
    }

    static String getStrValByRow(Row r, CommonUpload e) {
        return getStrValByRow(r, e, new HashMap<>());
    }

    static String getStrValByRow(Row r, CommonUpload e, Map<String, Integer> headIndexMap) {
//        if (!CollectionUtil.contains(headIndexMap.keySet(), e.getColumnName())) {
//            return null;
//        }
        return getValueByRow(r, e, headIndexMap) == null ? "" : getValueByRow(r, e, headIndexMap).toString();
    }

    static String getValueByIndex(Row r, Integer i) {
        if (r == null || r.getCell(i) == null) {
            return "";
        }
        setCellStrType(r.getCell(i));
        return r.getCell(i).getStringCellValue().trim();
    }

    static Object getValueByRow(Row r, CommonUpload e) {
        return getValueByRow(r, e, new HashMap<>());
    }

    static Object getValueByRow(Row r, CommonUpload e, Map<String, Integer> headIndexMap) {
        Integer index = e.getIndex();
        if (Objects.equals(index, Integer.MAX_VALUE)) {
            if (!CollectionUtil.contains(headIndexMap.keySet(), e.getColumnName())) {
                return null;
            }
            index = headIndexMap.get(e.getColumnName());
        }
        if (ObjectUtil.isNull(r) || ObjectUtil.isNull(r.getCell(index))) {
            return null;
        }
        setCellStrType(r.getCell(index), e);
        try {
            if (Objects.equals(e.getValueType(), Integer.class)) {
                return Integer.parseInt(r.getCell(index).getStringCellValue().trim());
            } else if (Objects.equals(e.getValueType(), Long.class)) {
                return Long.parseLong(r.getCell(index).getStringCellValue().trim());
            } else if (Objects.equals(e.getValueType(), Date.class)) {
                return r.getCell(index).getDateCellValue();
            }
        } catch (Exception ignored) {
            r.getCell(index).setCellType(CellType.STRING);
        }
        return r.getCell(index).getStringCellValue().trim();
    }

    static void setCellStrType(Cell cell, CommonUpload e) {
        if (ObjectUtil.isNull(cell)) {
            return;
        }
        if (!(ObjectUtil.isNotNull(e) && Objects.equals(e.getValueType(), Date.class))) {
            if (!Objects.equals(cell.getCellType(), CellType.STRING)) {
                cell.setCellType(CellType.STRING);
            }
        }
    }

    static void exportToXlsx(HttpServletResponse response, String sheetName, String fileName, List<List<Object>> dataList) throws IOException {
//        response.addHeader("filename", fileName + ".xlsx");
//        response.addHeader("Cache-Control", "max-age=0");

        // 创建Workbook对象
        Workbook workbook = new XSSFWorkbook();

        // 创建表单
        Sheet sheet = workbook.createSheet(sheetName);

        AtomicInteger rowIdx = new AtomicInteger();
        dataList.forEach(
                data -> {
                    Row row = sheet.createRow(rowIdx.getAndIncrement());
                    for (int i = 0; i < data.size(); i++) {
                        Cell cell = row.createCell(i);
                        if (data.get(i) instanceof Date) {
                            cell.setCellValue(DateUtil.formatDateTime((Date) data.get(i)));
                        } else {
                            cell.setCellValue(String.valueOf(data.get(i)));
                        }
                    }
                }
        );

        workbook.write(response.getOutputStream());
        response.getOutputStream().flush();
        workbook.close();
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");
        response.setHeader("filename", fileName + ".xlsx");
    }

    static void setCellStrType(Cell cell) {
        setCellStrType(cell, null);
    }

    static List<Row> getRowsByFileAndRowIndex(byte[] buffer, Integer rowIndex) throws Exception {
        return getRowsByFileAndRowIndex(buffer, rowIndex, null);
    }

    static <E extends CommonUpload> List<Row> getRowsByFileAndRowIndex(byte[] bytes, Integer rowIndex, E[] values) throws Exception {
        return getRowsByFileAndRowIndex(bytes, rowIndex, values, false);
    }

    static <E extends CommonUpload> List<Row> getRowsByFileAndRowIndex(byte[] bytes, Integer rowIndex, E[] values, Boolean isCheckFormat) throws Exception {
        Workbook wb = null;
        try {
            wb = new XSSFWorkbook(new ByteArrayInputStream(bytes));
        } catch (Exception ignored) {
            wb = new HSSFWorkbook(new ByteArrayInputStream(bytes));
        }
        Sheet sheet = wb.getSheetAt(0);
        List<Row> rows = ObjectFieldHandler.generateFindAllOptional(sheet, r -> r.getRowNum() > rowIndex && checkRowOptional(values, r, isCheckFormat));

        if (CollectionUtil.isEmpty(rows)) {
            throw new Exception("文件为空，无法解析");
        }
        return rows;
    }

    static Sheet getSheet0ByFile(byte[] b) throws Exception {
        Workbook wb = null;
        try {
            wb = new XSSFWorkbook(new ByteArrayInputStream(b));
        } catch (Exception ignored) {
            wb = new HSSFWorkbook(new ByteArrayInputStream(b));
        }
        Sheet sheet = wb.getSheetAt(0);
        if (Objects.equals(sheet.getSheetName(), "desc")) {
            return wb.getSheetAt(1);
        }
        return sheet;
    }

    static List<Row> getRowsBySheet(Sheet sheet, Integer rowIndex) throws Exception {
        List<Row> rows = ObjectFieldHandler.generateFindAllOptional(sheet, r -> r.getRowNum() >= rowIndex && checkRowOptional(r));

        if (CollectionUtil.isEmpty(rows)) {
            throw new Exception("The contents in the file are empty, please check.");
        }
        return rows;
    }

    @Deprecated
    /**
     * @author: lw
     * @createTime: 2024-06-12 10:55:50
     * @description:
     * @param: response - [HttpServletResponse]
     * @param: sheetName - [String]
     * @param: fileName - [String]
     * @param: dataList - [Object>>]
     *  some extension error would appear when use this function, wait to solve it.
     */
    static void exportToCSVPoi(HttpServletResponse response, String sheetName, String fileName, List<List<Object>> dataList) throws IOException {
        if (StringUtils.isAnyBlank(sheetName, fileName)) {
            throw new RuntimeException("sheet name or file name shouldn't be empty");
        }
        if (dataList == null) {
            dataList = new ArrayList<>();
        }
//        Workbook workbook = WorkbookFactory.create(true);
        Workbook workbook = new HSSFWorkbook();


        Sheet sheet = workbook.createSheet(sheetName);
        for (int i = 0; i < dataList.size(); i++) {
            Row row = sheet.createRow(i);
            List<Object> data = dataList.get(i);
            for (int j = 0; j < data.size(); j++) {
                Cell cell = row.createCell(j);
                cell.setCellValue(String.valueOf(data.get(j)));
            }
        }

        // 设置响应头
        response.setHeader("Content-Disposition", "attachment; filename=\"data.csv\"");
        response.setContentType("text/csv");

        // 写入响应流
        try (OutputStream outputStream = response.getOutputStream()) {
            workbook.write(outputStream);
            outputStream.flush();
        }

//        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//        workbook.write(outputStream);
//        workbook.close();
//
//        response.setContentType("text/csv");
//        response.setHeader(CONTENT_DISPOSITION, "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.displayName()) + ".csv;");
//        response.setCharacterEncoding("UTF-8");
//
//
//        response.getOutputStream().write(outputStream.toByteArray());
//        response.getOutputStream().flush();
//        response.getWriter().flush();
//        response.getWriter().close();
    }

}
